#include "net.h"
#include "ip.h"
#include "ethernet.h"
#include "arp.h"
#include "icmp.h"

int ip_id = 0;
/**
 * @brief 处理一个收到的数据包
 * 
 * @param buf 要处理的数据包
 * @param src_mac 源mac地址
 */
void ip_in(buf_t *buf, uint8_t *src_mac)
{
    // TO-DO
    ip_hdr_t* ip_hdr = (ip_hdr_t*)buf->data;
    if (buf->len < ip_hdr->hdr_len) {
        printf("\nip_in: buf->len < ip_hdr->hdr_len\n");
        return;
    }
    else if (ip_hdr->version != IP_VERSION_4) {
        printf("\nip_in: ip_hdr->version != IP_VERSION_4\n");
        return;
    }
    else if (swap16(ip_hdr->total_len16) > buf->len){
        printf("\nip_in: ip_hdr->total_len16 > buf->len\n");
        return;
    }

    uint16_t unchecked = ip_hdr->hdr_checksum16;
    ip_hdr->hdr_checksum16 = 0;

    uint16_t checked = checksum16((uint16_t *)ip_hdr, ip_hdr->hdr_len * 4);

    if (unchecked != checked)
    {
        printf("\nip_in: checksum error\n");
        printf("unchecked: %x\n", unchecked);
        printf("checked: %x\n", checked);
        return;
    }
    else{
        ip_hdr->hdr_checksum16 = checked;
    }

    if (memcmp(ip_hdr->dst_ip, net_if_ip, NET_IP_LEN)){
        printf("\nip_in: dst_ip is not net_if_ip\n");
        return;
    }

    if (buf->len > swap16(ip_hdr->total_len16)){
        buf_remove_padding(buf, buf->len - swap16(ip_hdr->total_len16));
    }
    
    if (ip_hdr->protocol != NET_PROTOCOL_ICMP &&
        ip_hdr->protocol != NET_PROTOCOL_UDP)
    {
        icmp_unreachable(buf, ip_hdr->src_ip, ICMP_CODE_PROTOCOL_UNREACH);
    }
    else{
        buf_remove_header(buf, ip_hdr->hdr_len * 4);
        net_in(buf, ip_hdr->protocol, ip_hdr->src_ip);
    }
}

/**
 * @brief 处理一个要发送的ip分片
 * 
 * @param buf 要发送的分片
 * @param ip 目标ip地址
 * @param protocol 上层协议
 * @param id 数据包id
 * @param offset 分片offset，必须被8整除
 * @param mf 分片mf标志，是否有下一个分片
 */
void ip_fragment_out(buf_t *buf, uint8_t *ip, net_protocol_t protocol, int id, uint16_t offset, int mf)
{
    // TO-DO
    buf_add_header(buf, sizeof(ip_hdr_t));
    ip_hdr_t *ip_hdr = (ip_hdr_t *)buf->data;
    ip_hdr->hdr_len = 5;
    ip_hdr->version = IP_VERSION_4;
    ip_hdr->tos = 0;
    ip_hdr->total_len16 = swap16(buf->len);
    ip_hdr->id16 = swap16(id);

    ip_hdr->flags_fragment16 = swap16((mf << 13) | ((offset & 0x1fff) >> 3));
    ip_hdr->ttl = 64;
    ip_hdr->protocol = protocol;
    memcpy(ip_hdr->dst_ip, ip, NET_IP_LEN);
    memcpy(ip_hdr->src_ip, net_if_ip, NET_IP_LEN);

    ip_hdr->hdr_checksum16 = 0;
    uint16_t checked = checksum16((uint16_t *)ip_hdr, ip_hdr->hdr_len * 4);
    ip_hdr->hdr_checksum16 = checked;
    arp_out(buf, ip);
}

/**
 * @brief 处理一个要发送的ip数据包
 * 
 * @param buf 要处理的包
 * @param ip 目标ip地址
 * @param protocol 上层协议
 */
void ip_out(buf_t *buf, uint8_t *ip, net_protocol_t protocol)
{
    // TO-DO
    size_t len = buf->len;
    size_t unit = ETHERNET_MAX_TRANSPORT_UNIT - sizeof(ip_hdr_t);
    if (len > unit){
        int cnt = 0;
        int offset = 0;
        while(len > unit){
            buf_t ip_buf;
            buf_init(&ip_buf, unit);
            offset = cnt * unit;
            memcpy(ip_buf.data, buf->data + offset, unit);
            ip_fragment_out(&ip_buf, ip, protocol, ip_id, offset, 1);
            len -= unit;
            cnt++;
        }
        if (len > 0) {
            buf_t ip_buf;
            buf_init(&ip_buf, len);
            offset = cnt * unit;
            memcpy(ip_buf.data, buf->data + offset, len);
            ip_fragment_out(&ip_buf, ip, protocol, ip_id, offset, 0);
        }
        ip_id++;
    }
    else{
        ip_fragment_out(buf, ip, protocol, ip_id++, 0, 0);
    }
}

/**
 * @brief 初始化ip协议
 * 
 */
void ip_init()
{
    net_add_protocol(NET_PROTOCOL_IP, ip_in);
}